7. 计算机编码问题

本节主要讲计算机的编码存储格式和相应的变换。

##  机器数和真值

先来说两个概念:

- 机器数:在计算机中的二进制表示形式,是可以表示符号的,比如正负数
- 真值: 是计算机内真正的那个值,因为一般有一个`bit`表示符号位,所以看到的和实际表示并不一致
https://blog.csdn.net/qq_48486897/article/details/106653164

    # 计算机所有存储都是补码,得到结果也是补码
    a = 3
    b = 5
    print("3&b = {0}".format(a&b))

    print("-1 & -2 = {0}".format(-1&-2))

    print("-2 & 6 = {0}".format(-2 & 6))

    # 位运算在乘除法中的应用
    print(" 1x2={0}".format(1<<1))
    print(" 1x16={0}".format(1<<4))
    print(" -10x4={0}".format(-10<<2))

    print(" -40/4={0}".format(-40>>2))

    # 二进制表示法

    print("10={0}".format(bin(10)))
    print("-10={0}".format(bin(-10)))



# 2. 原码
- 符号位加上真值的绝对值
    ```angularjs
    (+1)原码 = 0000 0001
    (-1)原码 = 1000 0001 
    八位取值范围是[1111 1111 - 0111 1111] = [-127 - 127]
    ```
    
# 3. 反码
- 正数的反码是其本身
- 负数的反码是在原码的基础上,符号位不变,其余各位取反
    ```angularjs
    [+1] = [0000 0001]原码 = [0000 0001]反码
    [-1] = [1000 0001]原码 = [1111 1110]反码
    ```
# 4. 补码
- 正数的补码是本身
- 负数的补码是原码基础上,符号位不变,各位取反,然后加1,即反码+1

# 5. 移码
- 补码的符号位取反

#  5. 为什么使用
- 1-1的问题
    ```angularjs
     (1 - 1)原码表示 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2 
     (1 - 1)反码表示 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
     (1 - 1)补码表示 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
     (-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补
    ```
- -128只是一个表示方法,是-0表示的-128,不是计算出来的,计算出来的是错误的, 
    ```angularjs
    [1000 0000]补码 = [0000 0000]原码 = 错误
    ```
- 补码,修复了0的两个表示方法带来的问题,还可以表示一个最低位,所以八位二进制表示范围是[-128,127]

# 编码问题
- 为什么需要编码
    - 计算机本质上只能表示二进制
    - 如何用一长串二进制表示复杂的信息
- 编码简史
    - 二进制:
        - 数字->字符->数字
        - 'a' -> 97 编码和解码
    - 第一阶段: ASCII
    - 第二阶段:百花齐放,GB2312,GBK, BIG5 Latin1, ISO-8859-1, JIS, ANSI...
        - Latin1(ISO-8859-1)
        - 中国GBxxx
        - 韩国台湾Big5
        - 日本JIS
        - ANSI-MBCS(Muilti-Bytes Charecter Set,多字节字符集)
    - Unicode+ISO
# 编码表示法
- ASCII-American Standard Code for Information Interchange
        - 所有的控制字符(比如CR回车、DEL删除等)编码在0-31范围以及127中。
        - 把所有的标点符号,英文大小写全部放在32-126范围中。
        - 防止以后出现需要补充的情况,把128-255位这么多位置留出来,
        - 0xxxx xxxx, 高位为0
- Latin1
    - 0-127的所有位置不动,那么可以兼容ASCII,二进制位0xxx xxxx
    - 128-255位置全部用完,二进制位1xxx xxxx
        - 128-159之间为控制字符,
        - 160-255位文字符号,
        - 其中包括了西欧语言、希腊语、泰语、阿拉伯语、希伯来语
    - 欧元符号
    
- GBXXXXXXXXX
    - GB2312
        - 如果一个字节中第一位为0,那么这就是一个ASCII字符。
        -如果一个字节中第一位为1,那么这个是汉字,认定需要2个字节才表示一个编码的文字。
        - 这个码表中包含汉字6763个和非汉字图形字符682个。
        - 还有很多的空间没有用到,索性全部预留了吧。
        - 0xxxxxxx:表示为ASCII字符
        -1xxxxxxx 1xxxxxxx:表示为汉字
    - GBK
        - 在GB2312基础上添加汉字
        - 兼任GB2312和ASCII
        - 0xxxxxxx:表示为ASCII字符
        - 1xxxxxxx xxxxxxxx:表示为汉字
    - GB18030
        - 2/4位混编
        
- Unicode
    - 只是一个码表,具体实现没有规定
    - 0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符
    - 中文的编码范围为4E00-9FCF,其中9FC4-9FCF之间的区间没有使用
    - 上述区间全部是汉字,不包含全角字符,不包含特殊文字
    - UTF=UnicodeTransformationFormat
    - UTF-8 
     
            0x0000~0x007F  (0 ~ 127) 	1字节 	0xxxxxxx
            0x0080~0x07FF(128 ~ 2047)  	110xxxxx 10xxxxxx
            0x0800~FFFF(2048 ~ 65535) 	3字节 	1110xxxx 10xxxxxx 10xxxxxx
            0x10000~1FFFFFF(65536 ~ 2097152) 	4字节 	11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
            0x2000000~0x3FFFFFF 	5字节 	111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
            0x4000000~0x7FFFFFFF 	6字节 	1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    - UTF-16, 32
        - UTF-16是早期Unicode遗留产物
        - UTF-32浪费空间
- UCS-2
    - UCS=UniversalCharacterSet,通用字符集
    - UCS-2与Unicode相同
    - 采用2个字节,定长的表示每一个字符,所以总计可以表示2^16个字符
- UCS-4
    - 第一个字节:表示组(group),最高位为0,则有128个。
    - 第二个字节:表示平面(plane),256个。
    - 第三个字节:表示行(row),256个。
    - 第四个字节:表示码位(cell),256个
    - 如果UCS-4前两个字节为0, 则就是CUS-2

# 概念解释
- 大尾(BigEndian) 小尾(LittleEndian)
    - "汉" -> 6C49
    - 6C49 -> BigEndian
    - 496C -> LittleEndian
- BOM
    - UTF-8 无字节顺序问题
    - UTF-16会出现问题:
        - "奎" -> 594E
        - "乙" -> 4E59
    - BOM-ByteOrderMark
        - "ZERO WIDTH NO-BREAK SPACE" -> FEFF, 在UCS中不存在
        - FEFF->BigEndian
        - FFFE->LittleEndian
        - UTF-8 用来表示编码, FEFF的UTF-8编码是EF BB BF, 用来表示此后编码是UTF-8编码
# Python的编码问题
- str
- bytes
- bytearray
    
    
            >>> b = bytes.fromhex('E4 B8 AD')
            >>> b
            b'\xe4\xb8\xad'
            >>> b.decode('utf-8')
            '中'
            >>> str(b)
            "b'\\xe4\\xb8\\xad'"

        
        >>> ord('A')
        65
        >>> ord('中')
        20013
        >>> chr(65)
        'A'
        >>> chr(20013)
        '中'
- len的单位是字符,不是字节
- python文件默认utf-8编码,如果特殊需要,需要声明
    - 放在第一行,或者第二行
    - ```# ‐*‐ coding: windows‐1252 ‐*‐```
    - 读写文件默认utf-8, 可以指定
    - code point方式比较字符串,可能会带来问题
        -  重音符号的表示 
        -  使用 unicodedata.normalize 函数
- Python源码中出现了解码错误,那么会产生SyntaxError异常
- 其他情况下,如果发现编码解码错误,那么会产生UnicodeEncodeError, UnicodeDecodeError异常

# 参考资料
- https://www.cnblogs.com/jessonluo/p/4800331.html
- https://blog.csdn.net/xuejianhui/article/details/52576771
- http://tools.jb51.net/table/gb2312
-